<?php
namespace app\first\controller;
use Alipay\AopClient;
use Alipay\Request\AlipayTradeAppPayRequest;
use app\first\model\Member;
use app\first\model\OrderList;
use app\first\model\RechargeList;
use app\first\model\RechargeType;
use think\facade\Config;
use think\facade\Env;
use Yansongda\Pay\Pay;
use Yansongda\Pay\Log;
/**
 * Class Payment
 * @title 支付相关
 * @url /v1/payment
 * @desc  支付相关接口
 * @version 1.0
 * @package app\first\payment
 * @route('v1/payment')
 */
class Payment extends Base{
    //是否开启授权认证,默认开启
    public $apiAuth = false;
    protected $model = null;
    protected $validate = null;
    protected static $alypay = null;
    protected static $wechat = null;

    /**
    * 需要附加的其他方法,比如:topList,newList
    * protected $extraActionList = ['topList','newList',...];
    * 已经对核心的类进行了重写,扩展的新方法会自动添加到认证中,无需再手动书写
    */
    protected $extraActionList = [];
    /**
    *忽略验证的方法
    */
    protected $skipAuthActionList = [
        'notify',
        'recharge_type',
        'query_order',
        'cancel'
    ];

    public function __construct(){
        parent::__construct();
        $this->model = new \app\first\model\Payment;
        $this->validate = new \app\first\validate\Payment;
        self::$alypay = Pay::alipay(Config::get('alipay.'));
        self::$wechat = Pay::wechat(Config::get('wechat.'));
    }

    /**
     * @title 获取充值列表
     * @method recharge_type
     * @route('v1/payment/recharge_type','get')
     * @return object data
     * @return \think\facade\Response|\think\response\Json|\think\response\Jsonp|\think\response\Redirect|\think\response\Xml
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public function recharge_type(){
        $rechargeType = (new RechargeType())->all();
        return $this->sendSuccess($rechargeType);
    }

    /**
     * @title 支付签验（测试）
     * @method pay_signature
     * @param int $type 付款方式 true 2 1支付宝,2微信
     * @param int $pay_drive true  1 1APP,2小程序,3H5,4公众号,5扫码,6企业,7普通红包,8分裂红包,9刷卡支付,10手机（支付宝）,11网站（支付宝）
     * @return mixed
     * @route('v1/payment/pay_signature','get')
     */
    public function pay_signature($type=2,$pay_drive=1){
        if($type==1){
            switch (intval($pay_drive)){
                case 10:
                    $drive = 'wap';
                    break;
                case 11:
                    $drive = 'web';
                    break;
                case 9:
                    $drive = 'pos';
                    break;
                case 6:
                    $drive = 'transfer';
                    break;
                case 5:
                    $drive = 'scan';
                    break;
                case 4:
                    $drive = 'mp';
                    break;
                case 3:
                    $drive = 'wap';
                    break;
                case 2:
                    $drive = 'mini';
                    break;
                case 1:
                default:
                    $drive = 'app';
                    break;
            }
            $order = [
                'out_trade_no' => build_order_no(),
                'total_amount' => 0.01,
                'subject' => '测试'
            ];
            $pay1 = self::pay_alipay($order['out_trade_no'],$order['total_amount'],$order['subject']);
            $pay = self::$alypay->$drive($order)->getContent();
            return json([
                'status'=>1,
                'payment'=>$pay,
                'payment1'=>$pay1,
                'payment2'=>self::call_pay($order)
            ]);
        }else{
            switch (intval($pay_drive)){
                case 9:
                    $drive = 'pos';
                    break;
                case 8:
                    $drive = 'groupRedpack';
                    break;
                case 7:
                    $drive = 'redpack';
                    break;
                case 6:
                    $drive = 'transfer';
                    break;
                case 5:
                    $drive = 'scan';
                    break;
                case 4:
                    $drive = 'mp';
                    break;
                case 3:
                    $drive = 'wap';
                    break;
                case 2:
                    $drive = 'miniapp';
                    break;
                case 1:
                default:
                    $drive = 'app';
                    break;
            }
            $order = [
                'out_trade_no' => build_order_no(),
                'total_fee' => 0.01 * 100,
                'body' => '测试',
                'openid' => self::$user['open_id'],
            ];
            $pay = self::$wechat->$drive($order);
            return $pay->send();
        }
    }

    /**
     * @param int $type
     * @param string $open_id
     * @param string $out_trade_no
     * @param string $total_amount
     * @param string $subject
     * @return mixed|\Symfony\Component\HttpFoundation\Response
     */
    public function signature($type=1,$open_id='',$out_trade_no='',$total_amount='',$subject=''){
        if($type==1){
            $order = [
                'out_trade_no' => $out_trade_no,
                'total_fee' => $total_amount * 100,
                'body' => $subject,
                'openid' => $open_id?$open_id:self::$user['open_id'],
            ];
            return self::$wechat->app($order)->send();
        }else{
            $order = [
                'out_trade_no' => $out_trade_no,
                'total_amount' => $total_amount,
                'subject' => $subject
            ];
//            return self::$wechat->app($order)->getContent();
            return self::pay_alipay($out_trade_no,$total_amount,$subject,$subject,'','');
        }
    }

    /**
     * @param $order
     * @return string
     */
    public static function call_pay($order){
        $keyPair = \Alipay\Key\AlipayKeyPair::create(
            file_get_contents('../data/alipay_cert/private_key.pem'),
            file_get_contents('../data/alipay_cert/public_key.pem')
        );
        $aop = new AopClient(Config::get('alipay.app_id'),$keyPair);
        $aop->apiVersion = '1.0';
        $aop->signType = 'RSA2';
        $aop->postCharset='utf-8';
        $aop->format='json';
        $request = new AlipayTradeAppPayRequest();
        $request->setNotifyUrl(Config::get('alipay.notify_url'));
        $request->setBizContent(json_encode($order));
        $result = $aop->sdkExecute($request);
        return $result;
    }

    /**
     * @param $order_id
     * @param $price
     * @param $title
     * @param $order_body
     * @param string $product_code
     * @return mixed
     */
    public static function pay_alipay($order_id, $price, $title, $order_body='',$timeout_express='',$product_code='QUICK_MSECURITY_PAY'){
        require_once Env::get('app_path') . '../extend/alipay/AopSdk.php';
        $aop = new \AopClient();
        $aop->gatewayUrl = "https://openapi.alipay.com/gateway.do";
        $aop->appId = Config::get('alipay.app_id');
        $aop->format = "json";
        $aop->charset = "UTF-8";
        $aop->signType = "RSA2";
        $aop->rsaPrivateKey = Config::get('alipay.private_key');
        $aop->alipayrsaPublicKey =  Config::get('alipay.ali_public_key');
        //实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称：alipay.trade.app.pay
        $request = new \AlipayTradeAppPayRequest();
        $order_body = $order_body?:$title;
        $timeout_express = $timeout_express?:'90m';
        $bizcontent = \GuzzleHttp\json_encode([
            'body'=> $order_body,
            'subject'=> $title,
            'out_trade_no'=> $order_id,
            'timeout_express'=> $timeout_express,
            'total_amount'=> $price,
            'product_code'=> $product_code,
        ]);
        $request->setNotifyUrl(Config::get('alipay.notify_url'));
        $request->setBizContent($bizcontent);
        //这里和普通的接口调用不同，使用的是sdkExecute
        $response = $aop->sdkExecute($request);
        //htmlspecialchars是为了输出到页面时防止被浏览器将关键参数html转义，实际打印到日志以及http传输不会有这个问题
        //$out = htmlspecialchars($response);//就是orderString 可以直接给客户端请求，无需再做处理。
        $out['signature'] = $response;
        $out['order_id'] = $order_id;
        $out['status'] = 1;
        return $out;
    }

    /**
     * 企业付款
     * @param int $type 1微信,2支付宝
     * @param string $transfers_id 1open_id,2支付宝的会员ID
     * @param int $amount 金额
     * @param string $title 主题
     * @param string $desc 介绍
     * @param string $partner_trade_no 单号
     * @param string $re_user_name 真实姓名
     * @return mixed
     */
    public function transfers($type=1,$transfers_id='',$amount=0,$title='',$partner_trade_no='',$desc='',$re_user_name=''){
        if($type==1){
            $order = [
                'partner_trade_no' => $partner_trade_no,
                'check_name' => 'NO_CHECK',
                're_user_name' => $re_user_name,
                'openid' => $transfers_id,
                'amount' => ($amount * 100),
                'desc' => "{$title}-{$desc}"
            ];
            $pay = self::$wechat->transfer($order);
        }else{
            $order = [
                'out_biz_no' => $partner_trade_no,
                'trans_amount	' => $amount,
                'product_code' => 'TRANS_ACCOUNT_NO_PWD',
                'name' => $re_user_name,
                'order_title' => $title,
                'identity' => $transfers_id,
                'identity_type' => 'ALIPAY_USER_ID',
                'remark' => $desc
            ];
            $pay = self::$alypay->transfer($order);
        }
        return $pay;
    }

    /**
     * @title 会员充值
     * @method recharge
     * @param int $type 购买会员类型 true 1
     * @param int $pay_type 支付类型 true 1 1支付宝,2微信
     * @param string $openid openid false  微信支付选填
     * @param int $pay_drive true  1 1APP,2小程序,3H5,4公众号,5扫码,6企业,7普通红包,8分裂红包,9刷卡支付,10手机（支付宝）,11网站（支付宝）
     * @route('v1/payment/recharge','post')
     * @return \think\facade\Response|\think\response\Json|\think\response\Jsonp|\think\response\Redirect|\think\response\View|\think\response\Xml
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public function recharge($type=1,$pay_type=1,$openid='',$pay_drive=1){
        $openid = $openid?$openid:(new Member())->getOpenidByMid(self::$uid);
        $_type = (new RechargeType())->find($type);
        $recharge = new RechargeList();
        $title = "购买{$_type['title']}";
        $order_no = build_order_no1('cz_');
        $result = $recharge->addNew($title,self::$uid,$order_no,$_type['price'],$type,$pay_type,$_type['days'],$openid,$out);
        if(!$result){
            return $this->sendError(0,$out);
        }
        $_order_no_key = Config::get('app.order_type')['购买会员'];
        $pay= null;
        if($type==1){
            switch (intval($pay_drive)){
                case 10:
                    $drive = 'wap';
                    break;
                case 11:
                    $drive = 'web';
                    break;
                case 9:
                    $drive = 'pos';
                    break;
                case 6:
                    $drive = 'transfer';
                    break;
                case 5:
                    $drive = 'scan';
                    break;
                case 4:
                    $drive = 'mp';
                    break;
                case 3:
                    $drive = 'wap';
                    break;
                case 2:
                    $drive = 'mini';
                    break;
                case 1:
                default:
                    $drive = 'app';
                    break;
            }
            $order = [
                'out_trade_no' => build_order_no1($_order_no_key['支付宝']),
                'total_amount' =>  $_type['price'],
                'subject' =>  $title,
            ];
            $pay = self::$alypay->$drive($order);
        }else{
            switch (intval($pay_drive)){
                case 9:
                    $drive = 'pos';
                    break;
                case 8:
                    $drive = 'groupRedpack';
                    break;
                case 7:
                    $drive = 'redpack';
                    break;
                case 6:
                    $drive = 'transfer';
                    break;
                case 5:
                    $drive = 'scan';
                    break;
                case 4:
                    $drive = 'mp';
                    break;
                case 3:
                    $drive = 'wap';
                    break;
                case 2:
                    $drive = 'miniapp';
                    break;
                case 1:
                default:
                    $drive = 'app';
                    break;
            }
            $order = [
                'out_trade_no' => build_order_no1($_order_no_key['微信']),
                'total_fee' => $_type['price'] * 100,
                'body' => $title,
                'openid' => self::$user['open_id'],
            ];
            $pay = self::$wechat->$drive($order);
        }
        $out['signature']=$pay->send();
        return $this->sendSuccess($out);
    }

    /**
     * @title 取消订单
     * @method cancel
     * @param int $order_id 订单ID true
     * @param int $type 类型 true 1 1会员充值,2发布支付
     * @return \think\facade\Response|\think\response\Json|\think\response\Jsonp|\think\response\Redirect|\think\response\View|\think\response\Xml
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public function cancel($order_id=0,$type=1){
        $model = null;
        if($type){
            $model = new RechargeList();
        }else if($type==2){
//            $model = new OrderList();
        }else{
            return $this->sendError(0,'错误的支付类型');
        }
        $info = $model->field('create_time',true)->find($order_id);
        if(!$info){
            return $this->sendError('订单不存在');
        }
        if($info['status']!=1){
            return $this->sendError('订单已处理');
        }
        $info->status = -1;
        if(!$info->save()){
            return $this->sendError('订单取消失败');
        }
        return $this->sendSuccess('','订单取消成功');
    }

    /**
     * @title 订单查询
     * @method query_order
     * @param int $out_trade_no 订单号 true
     * @param int $type 类型 true 1 1支付宝,2微信
     * @route('v1/payment/query_order','get')
     * @return \think\facade\Response|\think\response\Json|\think\response\Jsonp|\think\response\Redirect|\think\response\View|\think\response\Xml
     * @throws \Yansongda\Pay\Exceptions\GatewayException
     * @throws \Yansongda\Pay\Exceptions\InvalidArgumentException
     * @throws \Yansongda\Pay\Exceptions\InvalidConfigException
     * @throws \Yansongda\Pay\Exceptions\InvalidSignException
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public function query_order($out_trade_no=0,$type=1){
//        $model = null;
//        if($type){
//            $model = new RechargeList();
//        }else if($type==2){
////            $model = new OrderList();
//        }else{
//            return $this->sendError(0,'错误的支付类型');
//        }
//        $info = $model->field('create_time',true)->where('out_trade_no','eq',$out_trade_no)->find();
//        if(!$info){
//            return $this->sendError('订单不存在');
//        }
        if($type==1){
            $result = self::$alypay->find($out_trade_no);
        }else{
           $result = self::$wechat->find($out_trade_no);
        }
        return $this->sendSuccess($result);
    }
    /**
     * @title 回掉验证
     * @method notify
     * @param int $type 类型 true 1 1支付宝,2微信
     * @route('v1/payment/notify','get')
     * @return \Symfony\Component\HttpFoundation\Response
     * @throws \Yansongda\Pay\Exceptions\InvalidArgumentException
     */
    public function notify($type=1){
        $payment = $type?self::$alypay:self::$wechat;
        $msg = $type==1?'Alipay notify':'Wechat notify';
        try{
            $data = $payment->verify(); // 是的，验签就这么简单！

            // 请自行对 trade_status 进行判断及其它逻辑进行判断，在支付宝的业务通知中，只有交易通知状态为 TRADE_SUCCESS 或 TRADE_FINISHED 时，支付宝才会认定为买家付款成功。
            // 1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号；
            // 2、判断total_amount是否确实为该订单的实际金额（即商户订单创建时的金额）；
            // 3、校验通知中的seller_id（或者seller_email) 是否为out_trade_no这笔单据的对应的操作方（有的时候，一个商户可能有多个seller_id/seller_email）；
            // 4、验证app_id是否为该商户本身。
            // 5、其它业务逻辑情况

            Log::debug($msg, $data->all());
        } catch (\Exception $e) {
            // $e->getMessage();
        }
        return $payment->success()->send();
    }
}